import { createParameterDecorator, ServerContext } from "maishu-node-mvc";
import { componetsDirectory } from "./website-config";
import path = require("path");
import fs = require("fs");
import ts = require("typescript");
import { ClassDeclaration, CallExpression, ObjectLiteralExpression, PropertyAssignment, StringLiteral } from "typescript";
import { ConnectionConfig } from "mysql";


let componetsDir = componetsDirectory;

export type MyServerContext = ServerContext & { componentInfos: ComponentInfo[] };

export let currentAppId = createParameterDecorator(async (req) => {
    let appId = req.headers["application-id"];
    return appId;
}) as Function;

export function getClientComponentInfos(sourcePath: string): ComponentInfo[] {

    let componentInfos: ComponentInfo[] = [];

    let staticPath = path.join(sourcePath, "static");
    let componentsPath = path.join(staticPath, componetsDir);

    console.assert(fs.existsSync(componentsPath));
    let dirs: string[] = [componentsPath];
    let files: string[] = [];
    while (dirs.length > 0) {
        let dir = dirs.pop();
        let items = fs.readdirSync(dir);
        for (let i = 0; i < items.length; i++) {
            let itemPath = path.join(dir, items[i])
            let stat = fs.statSync(itemPath);
            if (stat.isDirectory()) {
                dirs.push(itemPath);
            }
            else if (stat.isFile()) {
                files.push(itemPath);
            }
        }
    }

    files.forEach(fileName => {
        let tsSourceFile = ts.createSourceFile(
            fileName, fs.readFileSync(fileName).toString(),
            ts.ScriptTarget.ES5
        );

        let componentPath = path.relative(staticPath, fileName);
        componentPath = componentPath.replace(/\\/g, "/");
        componentPath = componentPath.replace(".tsx", ".js");

        let c = getFileComponentInfos(tsSourceFile, componentPath);
        componentInfos.push(...c);
    })


    function getFileComponentInfos(sourceFile: ts.SourceFile, filePath: string): ComponentInfo[] {
        let componentInfos: ComponentInfo[] = [];
        delintNode(sourceFile, filePath);
        return componentInfos;
    }

    function delintNode(node: ts.Node, filePath: string) {
        switch (node.kind) {
            case ts.SyntaxKind.ClassDeclaration:

                const DISPLAY_NAME: keyof ComponentInfo = "displayName";
                const NAME: keyof ComponentInfo = "name";
                const ICON: keyof ComponentInfo = "icon";
                const INTRODUCE: keyof ComponentInfo = "introduce";
                let propNames = [NAME, DISPLAY_NAME, ICON, INTRODUCE];

                let classDeclaration = node as any as ClassDeclaration;
                if (classDeclaration.decorators) {
                    var decorator = classDeclaration.decorators.filter(o => o.expression != null && (o.expression as CallExpression).expression != null
                        && ((o.expression as CallExpression).expression as StringLiteral).text == "component")[0];

                    if (decorator == null)
                        break;

                    let componentInfo = {} as ComponentInfo;

                    componentInfo.name = classDeclaration.name.text;
                    componentInfo.path = filePath;

                    var expr = decorator.expression as CallExpression;
                    let arg0 = expr.arguments[0] as ObjectLiteralExpression;
                    if (arg0 != null) {
                        let properties = arg0.properties.filter(o => o.kind == ts.SyntaxKind.PropertyAssignment) as PropertyAssignment[];
                        for (let i = 0; i < propNames.length; i++) {
                            let propName = propNames[i];
                            let prop = properties.filter(o => o.name.kind == ts.SyntaxKind.Identifier && (<ts.Identifier>o.name).text == propName)[0];
                            if (prop) {
                                componentInfo[propName] = (prop.initializer as ts.StringLiteral).text;
                            }
                        }
                    }
                    componentInfos.push(componentInfo);
                }
                break;
        }

        ts.forEachChild(node, (node) => delintNode(node, filePath));
    }

    return componentInfos;

}

export interface Settings {
    port: number,
    virtualPaths?: { [key: string]: string },
    db: ConnectionConfig,
    gateway: string,
}

export let settings: Settings = {} as Settings;
